package com.hisense.ovcloud.mqtt.warehouse.config;

import com.hisense.ovcloud.mqtt.warehouse.Constants;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.Row;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
public class WarehouseExecutor {
    @Resource(name = "dataSource") // 注入数据源
    private DataSource dataSource;

    public void directExecuteOneSQL(String sql) {
        DataSourceKey.use(Constants.DS.WAREHOUSE);
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {
            statement.execute(sql);
        } catch (SQLException e) {
            log.error("Failed to execute SQL, {}", e.getMessage(), e);
            throw new RuntimeException("Failed to execute SQL", e);
        }finally {
            DataSourceKey.clear();
        }
    }

    public long directExecuteQueryCount(String sqlQuery) {
        try {
            DataSourceKey.use(Constants.DS.WAREHOUSE);
            return Db.selectCount(sqlQuery);
        } catch (Exception e) {
            log.error("Failed to execute SQL, {}", e.getMessage(), e);
            throw new RuntimeException("Failed to execute SQL", e);
        }finally {
            DataSourceKey.clear();
        }
    }

    public <T> List<T> directExecuteQueryAndMapToBean(String sqlQuery, Class<T> clazz) {
        try {
            DataSourceKey.use(Constants.DS.WAREHOUSE);
            return Db.selectListBySql(sqlQuery).stream().map(row -> row.toEntity(clazz)).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("Failed to execute SQL, {}", e.getMessage(), e);
            throw new RuntimeException("Failed to execute SQL", e);
        }finally {
            DataSourceKey.clear();
        }
    }

    public <T> T directExecuteQueryOneAndMapToBean(String sqlQuery, Class<T> clazz) {
        try {
            DataSourceKey.use(Constants.DS.WAREHOUSE);
            Row row = Db.selectOneBySql(sqlQuery);
            if(row == null){
                return clazz.newInstance();
            }
            return row.toEntity(clazz);
        } catch (Exception e) {
            log.error("Failed to execute SQL, {}", e.getMessage(), e);
            throw new RuntimeException("Failed to execute SQL", e);
        }finally {
            DataSourceKey.clear();
        }
    }

//    public <T> List<T> directExecuteQueryAndMapToBeanV2(String sqlQuery, Class<T> clazz) {
//        DataSourceKey.use(Constants.DS.SHARDING_JDBC);
//        try (Connection connection = dataSource.getConnection();
//             Statement statement = connection.createStatement()) {
//            return BeanListConverter.convertToBeans(statement.executeQuery(sqlQuery), clazz);
//        }  catch (Exception e) {
//            log.error("Failed to execute SQL, {}", e.getMessage(), e);
//            throw new RuntimeException("Failed to execute SQL", e);
//        }finally {
//            DataSourceKey.clear();
//        }
//    }

    public Long directExecuteQueryCount2(String sqlQuery) {
        try {
            DataSourceKey.use(Constants.DS.WAREHOUSE);
            return Db.selectCount(sqlQuery);
        } catch (Exception e) {
            log.error("Failed to execute SQL, {}", e.getMessage(), e);
            throw new RuntimeException("Failed to execute SQL", e);
        }finally {
            DataSourceKey.clear();
        }
    }

    @SneakyThrows
    public boolean directExecuteSQLBatch(List<String> sqls) {
        DataSourceKey.use(Constants.DS.WAREHOUSE);
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {
            connection.setAutoCommit(false);
            sqls.forEach(sql -> {
                try {
                    statement.execute(sql);
                } catch (SQLException e) {
                    try {
                        log.error("Failed to batch execute SQL {}, {}, will rollback.", sql, e.getMessage(), e);
                        connection.rollback();
                    } catch (SQLException ex) {
                        log.error("Failed to rollback for sql: {}", sql, e);
                        throw new RuntimeException(ex);
                    }
                    log.error("Failed to execute sql: {}", sql, e);
                    throw new RuntimeException(e);
                }
            });
            connection.commit();
            return true;
        } catch (Exception e) {
            throw new RuntimeException("Failed to execute SQL", e);
        }finally {
            DataSourceKey.clear();
        }
    }

    @SneakyThrows
    public boolean directExecuteSQLBatchV2(List<String> sqls) {
        DataSourceKey.use(Constants.DS.WAREHOUSE);
        sqls.forEach(sql -> {
            try (Connection connection = dataSource.getConnection();
                 Statement statement = connection.createStatement()) {
                statement.execute(sql);
            } catch (SQLException e) {
                log.error("Failed to rollback for sql: {}", sql, e);
                throw new RuntimeException(e);
            } finally {
                DataSourceKey.clear();
            }
        });
        return true;
    }
}
